package main

import (
	"database/sql"
	"fmt"
	"log"

	_ "github.com/go-sql-driver/mysql" // MySQL驱动
)

/*

题目1：基本CRUD操作
假设有一个名为 students 的表，包含字段 id （主键，自增）、 name （学生姓名，字符串类型）、 age （学生年龄，整数类型）、 grade （学生年级，字符串类型）。
要求 ：
编写SQL语句向 students 表中插入一条新记录，学生姓名为 "张三"，年龄为 20，年级为 "三年级"。
编写SQL语句查询 students 表中所有年龄大于 18 岁的学生信息。
编写SQL语句将 students 表中姓名为 "张三" 的学生年级更新为 "四年级"。
编写SQL语句删除 students 表中年龄小于 15 岁的学生记录。


*/

// 学生结构体，方便扫描结果
type Student struct {
	ID    int
	Name  string
	Age   int
	Grade string
}

func connectDB(userName string, password string) (*sql.DB, error) {
	// 使用更完整的连接字符串，包含更多兼容性选项
	dsn := userName + ":" + password + "@tcp(localhost:3306)/?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := sql.Open("mysql", dsn)
	if err != nil {
		return nil, err
	}

	// 设置连接池参数
	db.SetMaxOpenConns(10)
	db.SetMaxIdleConns(5)

	return db, err
}

func createDb(db *sql.DB, dataBaseName string) error {

	var err error
	// 2. 创建数据库（如果已存在则忽略）
	if _, err = db.Exec("CREATE DATABASE IF NOT EXISTS " + dataBaseName + " DEFAULT CHARACTER SET utf8mb4"); err != nil {
		log.Fatalf("create db: %v", err)
	}
	// 切换到 dataBaseName
	if _, err = db.Exec("USE " + dataBaseName); err != nil {
		log.Fatalf("use %v: %v", dataBaseName, err)
	}
	return err
}

func createTable(db *sql.DB, tableName string) error {
	var err error
	// 3. 创建 students 表
	ddl := fmt.Sprintf(`
	CREATE TABLE IF NOT EXISTS %v (
		id INT AUTO_INCREMENT PRIMARY KEY,
		name VARCHAR(50) NOT NULL,
		age INT NOT NULL,
		grade VARCHAR(20) NOT NULL
	) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
	`, tableName)
	if _, err := db.Exec(ddl); err != nil {
		log.Fatalf("create table: %v", err)
	}
	return err
}

func inserTable(db *sql.DB, tableName string, name string, age int, Grade string) error {

	var err error
	// 4. 插入
	sql := fmt.Sprintf(`INSERT INTO %v(name, age, grade) VALUES (?, ?, ?)`, tableName)

	if _, err = db.Exec(sql,
		name, age, Grade); err != nil {
		log.Fatalf("insert: %v", err)
	}

	return err
}

func queryGreaterAgeTable(db *sql.DB, tableName string, age int) ([]Student, error) {

	var err error
	sql := fmt.Sprintf(`SELECT id, name, age, grade FROM %v WHERE age > ?`, tableName)

	rows, err := db.Query(sql, age)
	if err != nil {
		log.Fatalf("query: %v", err)
	}
	defer rows.Close()

	//查询所有大于18岁的数据

	students := make([]Student, 0)

	for rows.Next() {
		var s Student
		if err = rows.Scan(&s.ID, &s.Name, &s.Age, &s.Grade); err != nil {
			log.Fatalf("scan: %v", err)
		}
		students = append(students, s)

	}

	return students, err
}

/**/

func updateTable(db *sql.DB, tableName string, queryName string, updateGrade string) error {

	// 6. 更新
	var err error
	sql := fmt.Sprintf(`UPDATE %v SET grade = ? WHERE name = ?`, tableName)
	if _, err = db.Exec(sql, updateGrade, queryName); err != nil {
		log.Fatalf("update: %v", err)
	}
	return err

}

func deleteLessenAgeTable(db *sql.DB, tableName string, age int) error {

	// 6. 删除
	var err error
	sql := fmt.Sprintf(`DELETE FROM %v WHERE age < ?`, tableName)
	if _, err = db.Exec(sql, age); err != nil {
		log.Fatalf("delete: %v", err)
	}
	return err

}

/*
题目2：事务语句
假设有两个表： accounts 表（包含字段 id 主键， balance 账户余额）和
transactions 表（包含字段 id 主键， from_account_id 转出账户ID，
to_account_id 转入账户ID， amount 转账金额）。
要求 ：
编写一个事务，实现从账户 A 向账户 B 转账 100 元的操作。
在事务中，需要先检查账户 A 的余额是否足够，如果足够则从账户 A 扣除 100 元，
向账户 B 增加 100 元，并在 transactions 表中记录该笔转账信息。
如果余额不足，则回滚事务。

*/

func TransferMoney(db *sql.DB, fromAccountID, toAccountID string, amount float64) error {
	// 创建事务
	tx, err := db.Begin()
	if err != nil {
		return fmt.Errorf("开始事务失败: %v", err)
	}
	defer tx.Rollback() // 如果没有显式提交，则回滚

	// 1. 检查转出账户是否存在并获取余额
	var fromBalance float64
	err = tx.QueryRow("SELECT balance FROM accounts WHERE id = ?", fromAccountID).Scan(&fromBalance)
	if err != nil {
		if err == sql.ErrNoRows {
			return fmt.Errorf("转出账户 %s 不存在", fromAccountID)
		}
		return fmt.Errorf("查询转出账户余额失败: %v", err)
	}

	// 2. 检查转入账户是否存在
	var toBalance float64
	err = tx.QueryRow("SELECT balance FROM accounts WHERE id = ?", toAccountID).Scan(&toBalance)
	if err != nil {
		if err == sql.ErrNoRows {
			return fmt.Errorf("转入账户 %s 不存在", toAccountID)
		}
		return fmt.Errorf("查询转入账户失败: %v", err)
	}

	// 3. 检查余额是否足够
	if fromBalance < amount {
		return fmt.Errorf("账户 %s 余额不足，当前余额: %.2f，转账金额: %.2f",
			fromAccountID, fromBalance, amount)
	}

	// 4. 从转出账户扣除金额
	_, err = tx.Exec("UPDATE accounts SET balance = balance - ? WHERE id = ?",
		amount, fromAccountID)
	if err != nil {
		return fmt.Errorf("扣除转出账户金额失败: %v", err)
	}

	// 5. 向转入账户增加金额
	_, err = tx.Exec("UPDATE accounts SET balance = balance + ? WHERE id = ?",
		amount, toAccountID)
	if err != nil {
		return fmt.Errorf("增加转入账户金额失败: %v", err)
	}

	// 6. 记录转账交易信息
	_, err = tx.Exec(`INSERT INTO transactions (from_account_id, to_account_id, amount) 
        VALUES (?, ?, ?)`, fromAccountID, toAccountID, amount)
	if err != nil {
		return fmt.Errorf("记录交易信息失败: %v", err)
	}

	// 7. 提交事务
	err = tx.Commit()
	if err != nil {
		return fmt.Errorf("提交事务失败: %v", err)
	}

	log.Printf("转账成功: 从账户 %s 向账户 %s 转账 %.2f 元",
		fromAccountID, toAccountID, amount)
	return nil
}

// createAccountsTable 创建账户表
func createAccountsTable(db *sql.DB) error {
	ddl := `
    CREATE TABLE IF NOT EXISTS accounts (
        id VARCHAR(10) PRIMARY KEY,
        balance DECIMAL(10, 2) NOT NULL DEFAULT 0.00
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
    `
	if _, err := db.Exec(ddl); err != nil {
		return fmt.Errorf("创建accounts表失败: %v", err)
	}
	log.Println("accounts表创建成功")
	return nil
}

// createTransactionsTable 创建交易表
func createTransactionsTable(db *sql.DB) error {
	ddl := `
    CREATE TABLE IF NOT EXISTS transactions (
        id INT AUTO_INCREMENT PRIMARY KEY,
        from_account_id VARCHAR(10) NOT NULL,
        to_account_id VARCHAR(10) NOT NULL,
        amount DECIMAL(10, 2) NOT NULL,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        FOREIGN KEY (from_account_id) REFERENCES accounts(id),
        FOREIGN KEY (to_account_id) REFERENCES accounts(id)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
    `
	if _, err := db.Exec(ddl); err != nil {
		return fmt.Errorf("创建transactions表失败: %v", err)
	}
	log.Println("transactions表创建成功")
	return nil
}

// insertTestAccounts 插入测试账户数据
func insertTestAccounts(db *sql.DB) error {
	// 检查是否已有数据
	var count int
	db.QueryRow("SELECT COUNT(*) FROM accounts").Scan(&count)
	if count > 0 {
		log.Println("accounts表已有数据，跳过插入")
		return nil
	}

	// 插入测试账户
	accounts := []struct {
		id      string
		balance float64
	}{
		{"A", 1000.00}, // 账户A：1000元
		{"B", 500.00},  // 账户B：500元
		{"C", 200.00},  // 账户C：200元
	}

	for _, account := range accounts {
		_, err := db.Exec("INSERT INTO accounts (id, balance) VALUES (?, ?)",
			account.id, account.balance)
		if err != nil {
			return fmt.Errorf("插入账户 %s 失败: %v", account.id, err)
		}
		log.Printf("插入账户 %s，余额: %.2f", account.id, account.balance)
	}
	return nil
}

// queryAccountBalance 查询账户余额
func queryAccountBalance(db *sql.DB, accountID string) (float64, error) {
	var balance float64
	err := db.QueryRow("SELECT balance FROM accounts WHERE id = ?", accountID).Scan(&balance)
	if err != nil {
		if err == sql.ErrNoRows {
			return 0, fmt.Errorf("账户 %s 不存在", accountID)
		}
		return 0, fmt.Errorf("查询账户余额失败: %v", err)
	}
	return balance, nil
}

// queryTransactionHistory 查询交易历史
func queryTransactionHistory(db *sql.DB) error {
	rows, err := db.Query(`
        SELECT id, from_account_id, to_account_id, amount, created_at 
        FROM transactions 
        ORDER BY created_at DESC
    `)
	if err != nil {
		return fmt.Errorf("查询交易历史失败: %v", err)
	}
	defer rows.Close()

	log.Println("=== 交易历史 ===")
	for rows.Next() {
		var id int
		var fromID, toID string
		var amount float64
		var createdAt string

		if err := rows.Scan(&id, &fromID, &toID, &amount, &createdAt); err != nil {
			return fmt.Errorf("扫描交易记录失败: %v", err)
		}

		log.Printf("交易ID: %d, 从账户: %s, 到账户: %s, 金额: %.2f, 时间: %s",
			id, fromID, toID, amount, createdAt)
	}
	return nil
}
